"""
**Concept of MinStack:**
A MinStack is a specialized data structure that extends the functionality of a regular stack by providing an additional operation to retrieve the minimum element in constant time. It keeps track of the minimum value as elements are pushed and popped from the stack, ensuring efficient access to the minimum value at any given point.

**Properties of MinStack:**
1. Stack: The MinStack stores elements in a Last-In-First-Out (LIFO) manner, similar to a regular stack.
2. Min Stack: It includes an additional stack, called the "min stack," to keep track of the minimum elements at each stage.
3. Top Pointer: The MinStack maintains a top pointer to indicate the position of the top element in the stack.
4. Size Tracking: The stack tracks its size, representing the number of elements it currently holds.
5. Minimum Element: The MinStack maintains the minimum element at any given time, allowing constant-time access to it.
6. Push and Pop: Elements can be pushed onto the stack and popped from the top as in a regular stack.
7. Peek: The Peek operation retrieves the top element of the stack without removing it.
8. IsEmpty: Checks if the MinStack is empty.
9. Length: Returns the total number of elements in the MinStack.

**Operations of MinStack:**
1. Push: Inserts an element onto the top of the stack.
2. Pop: Removes and returns the element from the top of the stack.
3. Peek: Returns the element at the top of the stack without removing it.
4. Min: Retrieves the minimum element in the stack in constant time.
5. IsEmpty: Checks if the MinStack is empty.
6. Length: Returns the total number of elements in the MinStack.

The MinStack provides an efficient way to access the minimum element within a stack, making it useful in scenarios where the minimum value needs to be retrieved frequently. It combines the functionality of a stack with the ability to track the minimum element efficiently, ensuring optimal performance for applications that require such functionality.
"""


class MinStack:
    """
    MinStack is a data structure that supports constant-time retrieval of the minimum element.

    Attributes:
        stack (list): The underlying data structure to store elements.
        min_stack (list): The auxiliary stack to keep track of the minimum values.

    """

    def __init__(self):
        """
        Initialize an empty MinStack.
        """
        self.stack = []
        self.min_stack = []

    def push(self, item):
        """
        Pushes an element onto the stack.

        Args:
            item: The element to be pushed onto the stack.
        """
        self.stack.append(item)
        if len(self.min_stack) == 0 or item <= self.min_stack[-1]:
            self.min_stack.append(item)

    def pop(self):
        """
        Removes and returns the element from the top of the stack.

        Returns:
            The element popped from the top of the stack.
        """
        if len(self.stack) == 0:
            raise Exception("Stack Underflow")

        item = self.stack.pop()
        if item == self.min_stack[-1]:
            self.min_stack.pop()

        return item

    def peek(self):
        """
        Returns the element at the top of the stack without removing it.

        Returns:
            The element at the top of the stack.
        """
        if len(self.stack) == 0:
            return None
        return self.stack[-1]

    def get_min(self):
        """
        Returns the minimum element in the stack.

        Returns:
            The minimum element in the stack.
        """
        if len(self.min_stack) == 0:
            return None
        return self.min_stack[-1]

    def is_empty(self):
        """
        Checks if the stack is empty.

        Returns:
            True if the stack is empty, False otherwise.
        """
        return len(self.stack) == 0

    def __len__(self):
        """
        Returns the number of elements in the stack.

        Returns:
            The number of elements in the stack.
        """
        return len(self.stack)

    def __repr__(self):
        """
        Returns a string that represents a valid Python expression to recreate the MinStack.

        Returns:
            A string representation of the MinStack.
        """
        return f"MinStack(stack={self.stack}, min_stack={self.min_stack})"

    def __str__(self):
        """
        Returns a string representation of the MinStack.

        Returns:
            A string representation of the MinStack.
        """
        stack_str = ' -> '.join(str(item) for item in self.stack)
        min_stack_str = ' -> '.join(str(item) for item in self.min_stack)
        return f"Stack: (Bottom) {stack_str} (Top)\nMinStack: (Bottom) {min_stack_str} (Top)"


# Create a MinStack instance
min_stack = MinStack()

# Push elements onto the stack
min_stack.push(5)  # Pushing 5 onto the stack
min_stack.push(10)  # Pushing 10 onto the stack
min_stack.push(3)  # Pushing 3 onto the stack
min_stack.push(8)  # Pushing 8 onto the stack

# Print the stack and min_stack
print(min_stack)  # Stack: (Bottom) 5 -> 10 -> 3 -> 8 (Top)
                  # MinStack: (Bottom) 3 -> 5 (Top)

# Pop an element from the stack
popped_item = min_stack.pop()
print(f"Popped item: {popped_item}")  # Popped item: 8

# Peek at the top of the stack
top_item = min_stack.peek()
print(f"Top item: {top_item}")  # Top item: 3

# Get the minimum element in the stack
min_item = min_stack.get_min()
print(f"Minimum item: {min_item}")  # Minimum item: 3

# Check if the stack is empty
is_empty = min_stack.is_empty()
print(f"Is stack empty: {is_empty}")  # Is stack empty: False

# Get the number of elements in the stack
stack_size = len(min_stack)
print(f"Stack size: {stack_size}")  # Stack size: 3
